// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// TCP sockets

package net

import (
	"os"
	"syscall"
	"time"
)

type TCPListener struct {
	socketID int
	addr     *TCPAddr
}

func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
	if laddr == nil {
		return nil, &OpError{"listen", net, nil, errMissingAddress}
	}

	if ip4 := laddr.IP.To4(); ip4 != nil {
		sid, err := syscall.Socket(syscall.PF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
		if err != nil {
			return nil, &OpError{"socket", net, nil, err}
		}

		err = syscall.Bind(sid, laddr.toSockAddr4())
		if err != nil {
			return nil, &OpError{"bind", net, laddr, err}
		}

		// 16 is an arbitrary value. HelenOS doesn't provide any hint or limit.
		err = syscall.Listen(sid, 16)
		if err != nil {
			return nil, &OpError{"listen", net, laddr, err}
		}

		return &TCPListener{
			socketID: sid,
			addr:     laddr,
		}, nil
	}

	// TODO: support IPv6
	return nil, ip6NotSupportedYet
}

func (l *TCPListener) Accept() (c Conn, err error) {
	return l.AcceptTCP()
}

func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
	csid, addr, err := syscall.Accept(l.socketID)
	if err != nil {
		return nil, &OpError{"accept", "tcp", l.addr, err}
	}

	if csid == 0 {
		// FIXME: accept() has either wrong documentation or implementation; find out which
		panic("Something is wrong with accept()")
	}

	return &TCPConn{
		initialized: true,
		socketID:    csid,
		laddr:       l.addr,
		raddr:       sockAddrToTCPAddr(addr),
	}, nil
}

func (l *TCPListener) Addr() Addr {
	return l.addr
}

func (l *TCPListener) Close() error {
	if l.socketID == -1 {
		return nil
	}

	return syscall.CloseSocket(l.socketID)
}

func (l *TCPListener) File() (f *os.File, err error) {
	return nil, helenosSupportError
}

func (l *TCPListener) SetDeadline(t time.Time) error {
	// TODO: support deadline
	return helenosSupportError
}
